package main

import("fmt"
//    "net"
    "time"
    "unsafe"
    _ "github.com/go-sql-driver/mysql"
    "github.com/jmoiron/sqlx"
    "strconv"
    "bytes"
    "encoding/binary"
    "errors"
    "crypto/md5"
    "math/rand"
    "path"
    "strings"
    "os"
    "bufio"
    "io/ioutil"
   // "sync"
 //   "database/sql"
)

const(
    console_type_default = 0
    console_type_error = 1
    console_type_console=3
    console_type_debug=2
    console_type_application=4
    console_type_hidedate=16
    
)
var Db *sqlx.DB
var start_time int64;
var exit_t=false;
var debug bool
var r *rand.Rand
var ul=make(map [int64] *sockc)
var _Distribution_sequence_addr int64
type xer struct{
    Error interface{}
}
type client_data struct{
    lx uint8
    data []byte
}
type user struct {
    UID [16]byte
    password [16]byte
}

type machine_online_temp struct{
    name string 
    tcp int64
}

var sign_ping = make(map[string] chan int)
func CreatPing(PID string){
    sign_ping[PID]=make(chan int,1)//= append(sign_ping,a)
    fmt.Printf("%+v\n%+v\n", make(chan int,1),sign_ping[PID])
}
func WaitPing(PID string)int{
    Print_(0,"receving data")
    v:=<-sign_ping[PID]
    Print_(0,"receved data")
    
    return v//value//(int64)(timex)
}
func ClearPing(PID string){
    delete(sign_ping,PID)
}
func StopPing(PID string,values int){
    Print_(0,"Stop ping")
    //fmt.Printf("test %+v\n", sign_ping[PID])
    sign_ping[PID]<- values
    Print_(0,"Stoped ping")
    
}

func Ping_(UID string,conn *sockc,timeout int)int{
    PID := RandString(5)
    timex_ := get_times()
    CreatPing(PID)
    var err error
    if(conn==nil){conn,err=getsock(UID_get_sock(UID));if err != nil {return 0}}
    _,err=conn.Write(byte_add_to_bytes([]byte{1}, Int64ToBytes(timex_), ([]byte)(PID)))
    if(err !=nil){return 0}
    Set_timer_runfunction(timeout,func(){StopPing(PID,0)})
    timex := WaitPing(PID)
    return timex
}

func init() {
    r = rand.New(rand.NewSource(time.Now().Unix()))
}

func init_time(){
    start_time=time.Now().UnixNano() / 1e6
}
func Load_login_mysql(ip string,user string,password string,DBname string)bool{
    database, err := sqlx.Open("mysql", user+":"+password+"@tcp("+ip+")/"+DBname)
    if err != nil {
        fmt.Println("open mysql failed,", err)
        return false 
    }
   
    Db=database
    return true
}

func reg_login_deleteall(){
    conn, err := Db.Begin()
    if err != nil {fmt.Println("open mysql failed,", err)}
    _,errx:=conn.Exec("delete  from `online_temp`")
    if errx != nil {conn.Rollback();fmt.Println("open mysql failed,", errx)}
    conn.Commit()
    Print_(0,"clear database")
}
func reg_login_token(conn *sockc,UID string)bool{
   // var mot machine_online_temp
    var p []int64
    err := Db.Select(&p, "SELECT tcp FROM `online_temp` where name=?", UID)
    if err != nil {fmt.Println("exec failed, ", err)
    return false}
    for i:=0;i<len(p);i++{
        conn_,_:=getsock(p[i])
        go func(){/*Print_(0,"login aaaaaaaaaaaaaaaa");*/if(conn!=nil){
            SendMessage_Text(conn_,"close");/*Sleep(1000);*/
            conn_.Close();/*Print_(0,"disconnect client")*/}}()
        connx, err := Db.Begin()
        if err != nil {return false}
        connx.Exec("delete  from `online_temp` where name=?",UID)
        if err != nil {connx.Rollback();return false}
        connx.Commit()
    }
    _,err=Db.Exec("insert into online_temp(name,tcp) values(?,?)",UID,(int)(getsockaddr(conn)))
    Print_(console_type_debug,fmt.Sprintf("insert into online_temp(name,tcp) values(%s,%d)",UID,(int)(getsockaddr(conn))))
    return err==nil
}

func user_login_check(user string,password string)bool{
    n:=""
    err:=Db.Get(&n,"SELECT UID FROM `userlist` where UID=? and pass=?",user,password)
    if err != nil {return false}
    //Print_(0,"user"n)
    return n!=""
}

func MD5(str string) string {
    data := []byte(str)
    has := md5.Sum(data)
    md5str := fmt.Sprintf("%x", has)
    return md5str
}
func get_machine_Relate_online_user_sock(machine_ID string)([]int64,error){
    var p []int64
    err := Db.Select(&p,"SELECT tcp FROM `relation`,`online_temp` where online_temp.name=relation.uid and machineID=?",machine_ID)
    if err != nil {return nil,err}else{return p,nil}
}
func get_machine_Relate_user(machine_ID string)([]string,error){
    var p []string
    err := Db.Select(&p,"SELECT uid FROM `relation` where  machineID=?",machine_ID)
    if err != nil {return nil,err}else{return p,nil}
}
func UID_get_sock(uid string)int64{
    var p int64
    Print_(console_type_debug,"[UID_GET_SOCK]"+uid,strconv.Itoa(int(p)))
    if(uid==""){return 0}
    Db.Get(&p, "SELECT tcp FROM `online_temp` where name=?", uid)
    return p
}
func byte_add_to_bytes(s ...[]byte)[]byte{
    bytesBuffer := bytes.NewBuffer([]byte{})
    for i:=0;i<len(s);i++ {
        binary.Write(bytesBuffer, binary.BigEndian, s[i])
    }
    return bytesBuffer.Bytes()
}
func ints_to_Bytes(intx ...uint8)[]byte{
    bytesBuffer := bytes.NewBuffer([]byte{})
    for i:=0;i<len(intx);i++ {
        binary.Write(bytesBuffer, binary.BigEndian, intx[i])
    }
    return bytesBuffer.Bytes()
}

func Exec_SQL( sqlInfo string, args ...interface{}) ([]map[string]interface{}, error) {
	rows, err := Db.Query(sqlInfo, args...)
	if err != nil {
		return nil, err
	}
	columns, _ := rows.Columns()
	columnLength := len(columns)
	cache := make([]interface{}, columnLength) //临时存储每行数据
	for index, _ := range cache { //为每一列初始化一个指针
		var a interface{}
		cache[index] = &a
	}
	var list []map[string]interface{} //返回的切片
	for rows.Next() {
		_ = rows.Scan(cache...)
 
		item := make(map[string]interface{})
		for i, data := range cache {
			item[columns[i]] = *data.(*interface{}) //取实际类型
		}
		list = append(list, item)
	}
	_ = rows.Close()
	return list, nil
}

func Int64ToBytes(i int64) []byte {
    var buf = make([]byte, 8)
    binary.BigEndian.PutUint64(buf, uint64(i))
    return buf
}
func RandString(len int) string {
    bytes := make([]byte, len)
    for i := 0; i < len; i++ {
        b := r.Intn(26) + 97//65
        bytes[i] = byte(b)
    }
    return string(bytes)
}

func BytesToInt64(buf []byte) int64 {
    return int64(binary.BigEndian.Uint64(buf))
}

func get_times() int64{
    //nowx := 
    return time.Now().UnixNano() / 1e6-start_time;
}
func toclient(data *[]byte) *client_data{
    return *(**client_data)(unsafe.Pointer(data))
}
func toldt(data*[]byte)*user{
    return *(**user)(unsafe.Pointer(data))
}
func checknum(s string) bool { _, err := strconv.ParseFloat(s, 64) 
    return err == nil }
func getsockaddr(conn *sockc)int64{
    if(conn.id>0){return conn.id}
    /*for name,k:= range ul{
        if(k==conn){return name}
    }*/
    _Distribution_sequence_addr=_Distribution_sequence_addr+1
    ul[_Distribution_sequence_addr]=conn
    conn.id=_Distribution_sequence_addr
    /*
    strPointerInt:=fmt.Sprintf("%d", unsafe.Pointer(conn))
    i, _ := strconv.ParseInt(strPointerInt, 10, 0)*/
    //fmt.Println("original data is",strPointerInt,i )
    return _Distribution_sequence_addr
}
func getsock(sockaddr int64)(cli *sockc,err error){
    //var conn *net.Conn
    if(sockaddr<0){err=errors.New("sock get error") }
    if(ul[sockaddr]==nil){err=errors.New("sock get error not found") }
    cli=ul[sockaddr]
    return
    //if(sockaddr==0){return nil,errors.New("sock get error")}
    //return (net.Conn)(*(*(**net.Conn)(unsafe.Pointer(&sockaddr)))),nil
    //return (net.Conn)(*conn)
}
func get_all_online()[]machine_online_temp{
    //var x []machine_online_temp
    x := make([]machine_online_temp, 0)
    //err := Db.Select(&x, "SELECT name,tcp FROM `online_temp`")
    rows,err:=Db.Query("SELECT name,tcp FROM `online_temp`")
    if err != nil {fmt.Println("exec failed, ", err);return x}
    for rows.Next(){
        var k machine_online_temp
        err=rows.Scan(&k.name,&k.tcp)
        x=append(x,k)
    }
    return x
}
func Print_(type_ int,txt ...string){
    //type     1-15 前4位，错误类型，第五位是否隐藏日期
    time_ :="";
    _t:=0;
    _head:="";
    if(type_ & 16 == 16){
        time_="";
    }else{
        time_=fmt.Sprintf("%d",get_times())
        _p:=""
        for i:=0;i<10-len(time_);i++ {
            _p=_p+"0"
        }
        time_="[ "+ _p+time_+" ] "
    }
    _t = type_&15;
    if(_t==console_type_error){
        _head="[error] "
    }else if(_t==console_type_console){
        _head="[console] "
    }else if(_t==console_type_debug){
        if(!debug){return}
        _head="[DEBUG] "
    }else if(_t==console_type_application){
        _head="[application] "
    }


    for i:=0;i<len(txt);i++ {
        fmt.Printf("%s%s%s\n",time_,_head,txt[i])
    }
    
}
func Sleep(sleeptime int64){
    time.Sleep(time.Duration(sleeptime) * time.Millisecond)
    //invalid operation: int(sleeptime) * time.Millisecond (mismatched types int and time.Duration)
}
func Get_file_name(fullFilename string)string {
    //fmt.Println("fullFilename =", fullFilename)
    var filenameWithSuffix string
    filenameWithSuffix = path.Base(fullFilename)
    //fmt.Println("filenameWithSuffix =", filenameWithSuffix)
    var fileSuffix string
    fileSuffix = path.Ext(filenameWithSuffix)
    //fmt.Println("fileSuffix =", fileSuffix)

    //var filenameOnly string
    return strings.TrimSuffix(filenameWithSuffix, fileSuffix)
    //fmt.Println("filenameOnly =", filenameOnly)
}
func file_ReadLine(file_ string,lineNumber int) string{
    file, _ := os.Open(file_)
    fileScanner := bufio.NewScanner(file)
    lineCount := 1
    for fileScanner.Scan(){
        if lineCount == lineNumber{
            return fileScanner.Text()
        }
        lineCount++
    }
    defer file.Close()
    return ""
}
func Make_Txt(x string,t int)string{
    var a string
    for i:=0;i<t;i++{
        a=a+x
    }
    return a
}
func Broadcast_message(conn *sockc, UID string, data []byte) { //广播物联网设备的消息给各个客户端
	sock, err := get_machine_Relate_online_user_sock(UID)
	if err != nil {
		return
	}
	for i := 0; i < len(sock); i++ {
		connx, err := getsock(sock[i])
		if err != nil {
			continue
		}
		connx.Write(data)
	}
}
func SendMessage(conn *sockc,data []byte)(int,error){
    return conn.Write(byte_add_to_bytes([]byte{13},data))
}
func SendMessage_Text(conn *sockc,data string)(int,error){
    return conn.Write(byte_add_to_bytes([]byte{13},[]byte(data)))
}


func Set_timer_runfunction(timeout int,f func()){
    timer:= time.NewTimer(time.Duration(timeout) * time.Millisecond)
    go func() {//等触发时的信号
        <-timer.C
        f()}()
}
func Cli_read_line()[]string{
    line:=make([]string,100)
        fmt.Scanln(&line[0],&line[1],&line[2],&line[3],&line[4],&line[5],&line[6],&line[7],&line[8],&line[9],&line[10],&line[11],&line[12],&line[13],&line[14],&line[15],&line[16],&line[17],&line[18],&line[19],&line[20],&line[21],&line[22],&line[23],&line[24],&line[25],&line[26],&line[27],&line[28],&line[29],&line[30],&line[31],&line[32],&line[33],&line[34],&line[35],&line[36],&line[37],&line[38],&line[39],&line[40],&line[41],&line[42],&line[43],&line[44],&line[45],&line[46],&line[47],&line[48],&line[49],&line[50],&line[51],&line[52],&line[53],&line[54],&line[55],&line[56],&line[57],&line[58],&line[59],&line[60],&line[61],&line[62],&line[63],&line[64],&line[65],&line[66],&line[67],&line[68],&line[69],&line[70],&line[71],&line[72],&line[73],&line[74],&line[75],&line[76],&line[77],&line[78],&line[79],&line[80],&line[81],&line[82],&line[83],&line[84],&line[85],&line[86],&line[87],&line[88],&line[89],&line[90],&line[91],&line[92],&line[93],&line[94],&line[95],&line[96],&line[97],&line[98],&line[99])
        ii:=0
        for i:=len(line)-1;i>=0;i--{if(line[i]!=""){ii=i;break}}
        //ret:=make([]string,0)
        cli:=line[0]
        for i:=1;i<ii+1;i++ {cli=cli+" "+line[i]}
        return ParseCli_(cli)
}
func Cli_read_ToString()string{
    line:=make([]string,100)
        fmt.Scanln(&line[0],&line[1],&line[2],&line[3],&line[4],&line[5],&line[6],&line[7],&line[8],&line[9],&line[10],&line[11],&line[12],&line[13],&line[14],&line[15],&line[16],&line[17],&line[18],&line[19],&line[20],&line[21],&line[22],&line[23],&line[24],&line[25],&line[26],&line[27],&line[28],&line[29],&line[30],&line[31],&line[32],&line[33],&line[34],&line[35],&line[36],&line[37],&line[38],&line[39],&line[40],&line[41],&line[42],&line[43],&line[44],&line[45],&line[46],&line[47],&line[48],&line[49],&line[50],&line[51],&line[52],&line[53],&line[54],&line[55],&line[56],&line[57],&line[58],&line[59],&line[60],&line[61],&line[62],&line[63],&line[64],&line[65],&line[66],&line[67],&line[68],&line[69],&line[70],&line[71],&line[72],&line[73],&line[74],&line[75],&line[76],&line[77],&line[78],&line[79],&line[80],&line[81],&line[82],&line[83],&line[84],&line[85],&line[86],&line[87],&line[88],&line[89],&line[90],&line[91],&line[92],&line[93],&line[94],&line[95],&line[96],&line[97],&line[98],&line[99])
        ii:=0
        for i:=len(line)-1;i>=0;i--{if(line[i]!=""){ii=i;break}}
        //ret:=make([]string,0)
        cli:=line[0]
        for i:=1;i<ii+1;i++ {cli=cli+" "+line[i]}
        return cli
}
func File_Read_All(filepath string)[]byte{
    bin,_:=ioutil.ReadFile(filepath)
    return bin
}
func try(fun func())(a xer){
    defer func() {if err := recover(); err != nil {a.Error=err}}()
    fun()
    return
}
func (a xer)catch(handler func(interface{})){
    if(a.Error==nil){return}else{ handler(a.Error)}
}
func Try(fun func(), handler func(interface{})) {  
    defer func() {if err := recover(); err != nil { handler(err)}}()  
    fun()
}  
func Completion_id(name string)string{//用户名补齐16位
    return name+Make_Txt("0",16-len(name))
}